import pandas as pd
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.preprocessing import LabelEncoder
import matplotlib.pyplot as plt
import seaborn as sns

# 读取数据
data = pd.read_csv('../dataset/课程成绩数据集.csv', encoding='gbk')

# 显示数据基本信息
print("数据集基本信息:")
print(f"数据形状: {data.shape}")
print("\n前5行数据:")
print(data.head())
print("\n数据列名:")
print(data.columns.tolist())
print("\n数据类型:")
print(data.dtypes)
print("\n缺失值情况:")
print(data.isnull().sum())

# 数据预处理
# 创建是否通过考试的标签（假设60分及以上为通过）
data['是否通过'] = data['学生的最终考试成绩'].apply(lambda x: 1 if x >= 60 else 0)

# 编码分类变量
le_sex = LabelEncoder()
le_education = LabelEncoder()
le_internet = LabelEncoder()
le_activity = LabelEncoder()

data['性别_编码'] = le_sex.fit_transform(data['性别'])
data['教育水平_编码'] = le_education.fit_transform(data['父母的教育水平（高中、学士、硕士、博士）'])
data['是否有网_编码'] = le_internet.fit_transform(data['学生在家中是否可以上网'])
data['课外活动_编码'] = le_activity.fit_transform(data['学生是否参加课外活动'])

# 选择特征变量
features = ['每周平均学习时间', '出勤率', '以前考试的平均分', '性别_编码', '教育水平_编码', '是否有网_编码', '课外活动_编码']
X = data[features]
y = data['是否通过']

print(f"\n特征变量形状: {X.shape}")
print(f"目标变量形状: {y.shape}")

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)

print(f"\n训练集形状: {X_train.shape}")
print(f"测试集形状: {X_test.shape}")
print(f"训练集中通过率: {y_train.mean():.2%}")
print(f"测试集中通过率: {y_test.mean():.2%}")

# 训练K近邻模型
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)

# 预测
y_pred = knn.predict(X_test)

# 评估模型
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)

print(f"\n模型评估结果:")
print(f"准确率: {accuracy:.4f}")
print(f"精确率: {precision:.4f}")
print(f"召回率: {recall:.4f}")
print(f"F1分数: {f1:.4f}")

# 特征重要性分析（通过排列重要性）
from sklearn.inspection import permutation_importance

result = permutation_importance(knn, X_test, y_test, n_repeats=10, random_state=42)
importance_df = pd.DataFrame({
    '特征': features,
    '重要性': result.importances_mean
}).sort_values('重要性', ascending=False)

print(f"\n特征重要性排序:")
print(importance_df)

# 尝试不同的K值寻找最优参数
k_values = range(1, 21)
accuracies = []

for k in k_values:
    knn_temp = KNeighborsClassifier(n_neighbors=k)
    knn_temp.fit(X_train, y_train)
    y_pred_temp = knn_temp.predict(X_test)
    accuracies.append(accuracy_score(y_test, y_pred_temp))

# 找到最优K值
best_k = k_values[np.argmax(accuracies)]
best_accuracy = max(accuracies)

print(f"\n最优K值: {best_k}")
print(f"最优准确率: {best_accuracy:.4f}")

# 使用最优K值重新训练模型
best_knn = KNeighborsClassifier(n_neighbors=best_k)
best_knn.fit(X_train, y_train)
best_y_pred = best_knn.predict(X_test)

best_accuracy = accuracy_score(y_test, best_y_pred)
best_precision = precision_score(y_test, best_y_pred)
best_recall = recall_score(y_test, best_y_pred)
best_f1 = f1_score(y_test, best_y_pred)

print(f"\n最优模型评估结果:")
print(f"准确率: {best_accuracy:.4f}")
print(f"精确率: {best_precision:.4f}")
print(f"召回率: {best_recall:.4f}")
print(f"F1分数: {best_f1:.4f}")

# 预测示例
print(f"\n预测示例:")
sample_data = X_test.iloc[:5]
sample_predictions = best_knn.predict(sample_data)
sample_probabilities = best_knn.predict_proba(sample_data)

for i, (idx, row) in enumerate(sample_data.iterrows()):
    actual_score = data.loc[idx, '学生的最终考试成绩']
    actual_pass = "通过" if data.loc[idx, '是否通过'] == 1 else "未通过"
    predicted_pass = "通过" if sample_predictions[i] == 1 else "未通过"
    confidence = sample_probabilities[i][1] if sample_predictions[i] == 1 else sample_probabilities[i][0]
    
    print(f"样本 {i+1}: 实际成绩={actual_score}分, 实际={actual_pass}, "
          f"预测={predicted_pass}, 置信度={confidence:.2f}")

# 保存模型预测结果
results_df = pd.DataFrame({
    '实际标签': y_test,
    '预测标签': best_y_pred,
    '预测概率': best_knn.predict_proba(X_test)[:, 1]
})

print(f"\n预测结果统计:")
print(f"总样本数: {len(results_df)}")
print(f"正确预测数: {(results_df['实际标签'] == results_df['预测标签']).sum()}")
print(f"错误预测数: {(results_df['实际标签'] != results_df['预测标签']).sum()}")

# 特征相关性分析
correlation_matrix = data[['每周平均学习时间', '出勤率', '以前考试的平均分', '是否通过']].corr()
print(f"\n特征与目标变量的相关性:")
print(correlation_matrix['是否通过'].sort_values(ascending=False))